﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.OleDb;

namespace framework
{
    /// <summary>
    /// Manipulador do Excel
    /// </summary>
    [Serializable]
    public class Excel
    {
        private string _urlConnectionString;
        /// <summary>
        /// Construtor
        /// </summary>
        /// <param name="pathArquivo">Caminho + nome do arquivo</param>
        /// <param name="comCabecalho">
        /// Considerar a linha do cabeçalho<br/>
        /// <b>OBS: Caso false os campos devem ser recuperados pelo índice e não pelo nome</b>
        /// </param>
        public Excel(string pathArquivo, bool comCabecalho = true)
        {
            _urlConnectionString = string.Format(
                "Provider=Microsoft.Jet.OLEDB.4.0;Data Source={0};Extended Properties='Excel 8.0;HDR={1};'",
                pathArquivo,
                comCabecalho ? "YES" : "NO"
            );
        }

        /// <summary>
        /// Recupera uma Lista de Itens do intervalo
        /// </summary>
        /// <typeparam name="TipoItem">Tipagem do item da lista</typeparam>
        /// <param name="intervalo">Intervalo na planilha do Excel</param>
        /// <param name="metodoMontagem">Método de montagem do item da listagem</param>
        /// <returns>Lista montada</returns>
        public List<TipoItem> getTodos<TipoItem>(Intervalo intervalo, Func<IDataRecord, TipoItem> metodoMontagem)
        {
            using (ADOFabrica _fabrica = new ADOFabrica(_urlConnectionString, intervalo))
            {
                OleDbDataReader _reader = _fabrica.getReader(CommandBehavior.SequentialAccess);
                List<TipoItem> _retorno = new List<TipoItem>();
                while (_reader.Read())
                    _retorno.Add(metodoMontagem(_reader));
                return _retorno;
            }
        }
        /// <summary>
        /// Recupera o primeiro item do intervalo
        /// </summary>
        /// <typeparam name="TipoItem">Tipagem do item</typeparam>
        /// <param name="intervalo">Intervalo na planilha do Excel</param>
        /// <param name="metodoMontagem">Método de montagem do item</param>
        /// <returns>Item montado</returns>
        public TipoItem getPrimeiro<TipoItem>(Intervalo intervalo, Func<IDataRecord, TipoItem> metodoMontagem)
        {
            using (ADOFabrica _fabrica = new ADOFabrica(_urlConnectionString, intervalo))
            {
                OleDbDataReader _reader = _fabrica.getReader(CommandBehavior.SingleRow);
                return _reader.Read() ? metodoMontagem(_reader) : default(TipoItem);
            }
        }

        /// <summary>
        /// Gera os componentes ADO e no Dispose lemera os recursos
        /// </summary>
        [Serializable]
        private class ADOFabrica :IDisposable
        {
            private OleDbConnection _conexao;
            private OleDbCommand _commando;
            private OleDbDataReader _reader;

            /// <summary>
            /// Cria o DataReader
            /// </summary>
            /// <param name="modo">Como percorrer os dados</param>
            /// <returns>OleDbDataReader</returns>
            public OleDbDataReader getReader(CommandBehavior modo)
            {
                _reader = _commando.ExecuteReader(modo);
                return _reader;
            }

            /// <summary>
            /// Construtor
            /// </summary>
            /// <param name="urlConexao">ConnectionString</param>
            /// <param name="intervalo">Intervalo a ser pesquisado</param>
            public ADOFabrica(string urlConexao,Intervalo intervalo)
            {
                _conexao = new OleDbConnection(urlConexao);
                _conexao.Open();
                //...
                string _sql = String.Format("select * from [{0}]", intervalo);
                _commando = new OleDbCommand(_sql, _conexao);
            }

            /// <summary>
            /// Libera os recursos
            /// </summary>
            public void Dispose()
            {
                if (_reader != null)
                {
                    _reader.Close();
                    _reader.Dispose();
                }
                if (_commando != null)
                    _commando.Dispose();
                if (_conexao != null)
                {
                    _conexao.Close();
                    _conexao.Dispose();
                }
            }
        }
    }
}
